package com.atguigu.crowd.mvc.config;

import com.atguigu.crowd.constant.CrowdConstant;
import com.atguigu.crowd.exception.AccessForbiddenException;
import com.atguigu.crowd.exception.LoginAcctAlreadyInUseException;
import com.atguigu.crowd.exception.LoginAcctAlreadyInUseForUpdateException;
import com.atguigu.crowd.exception.LoginFailedException;
import com.atguigu.crowd.util.CrowdUtil;
import com.atguigu.crowd.util.ResultEntity;
import com.google.gson.Gson;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 基于注解的异常映射
 */
// @ControllerAdvice表示当前类是一个基于注解的异常处理器类
@ControllerAdvice
public class CrowdExceptionResolver {

    /**
     * 用户维护页 更新用户时违反账号唯一约束  有重复现象
     * @param exception
     * @param request
     * @param response
     * @return
     * @throws IOException
     */
    @ExceptionHandler(value = LoginAcctAlreadyInUseForUpdateException.class)
    public ModelAndView LoginAcctAlreadyInUseForUpdateException(
            LoginAcctAlreadyInUseForUpdateException exception,
            HttpServletRequest request,
            HttpServletResponse response
    ) throws IOException {

        //回到系统错误页面
        String viewName = "system-error";
        return commonResolve(viewName, exception, request, response);
    }
    /**
     * 新增用户 账号重复异常处理
     * @param exception
     * @param request
     * @param response
     * @return
     * @throws IOException
     */
    @ExceptionHandler(value = LoginAcctAlreadyInUseException.class)
    public ModelAndView LoginAcctAlreadyInUseException(
            LoginAcctAlreadyInUseException exception,
            HttpServletRequest request,
            HttpServletResponse response
    ) throws IOException {

        //回到增加页面
        String viewName = "admin-add";

        return commonResolve(viewName, exception, request, response);
    }
    /**
     * 原未登录状态访问受保护页面抛出的异常
     * spring security接管了 未登录不能访问受保护页面
     * 现改造这个方法作为 不允许访问页面（没权限）
     * @param exception
     * @param request
     * @param response
     * @return
     * @throws IOException
     */
    @ExceptionHandler(value =Exception.class)
    public ModelAndView Exception(
            Exception exception,
            HttpServletRequest request,
            HttpServletResponse response
    ) throws IOException {

        String viewName = "system-error";

        return commonResolve(viewName, exception, request, response);
    }

    /**
     * 登录失败异常处理 登录失败 回到登录页面
     * @param exception
     * @param request
     * @param response
     * @return
     * @throws IOException
     */
    @ExceptionHandler(value = LoginFailedException.class)
    public ModelAndView LoginFailedException(
            LoginFailedException exception,
            HttpServletRequest request,
            HttpServletResponse response
    ) throws IOException {

        String viewName = "admin-login";

        return commonResolve(viewName, exception, request, response);
    }

    // @ExceptionHandler将一个具体的异常类型和一个方法关联起来
    @ExceptionHandler(value = NullPointerException.class)
    public ModelAndView resolveNullPointerException(
            NullPointerException exception,
            HttpServletRequest request,
            HttpServletResponse response) throws IOException {

        //指定要返回的页面
        String viewName="system-error";
      return   commonResolve(viewName,exception,request,response);
    }

    //通用方法 将重复代码抽取出来
    private  ModelAndView commonResolve(String viewName,   //指定要返回的页面
                                        //实际捕获到的异常类型
                                        Exception exception,
                                        //当前请求对象
                                        HttpServletRequest request,
                                        //当前响应对象
                                        HttpServletResponse response) throws IOException {
        //获取异常消息

        //1.判断请求类型
        boolean judgeRequestType = CrowdUtil.judgeRequestType(request);

        //2.ajax请求 不返回页面
        // 将异常消息封装到返回结果对象ResultEntity
        // 并转换成JSON字符串响应给浏览器
        if(judgeRequestType){
            //3.创建ResultEntity对象
            ResultEntity<Object> resultEntity = ResultEntity.failed(exception.getMessage());

            //4.转成JSON 创建Gson对象
            Gson gson=new Gson();

            //5.ResultEntity转成JSON字符串
            String json = gson.toJson(resultEntity);

            //6.将JSON字符串作为响应体返回给浏览器
            response.getWriter().write(json);

            //7.上面使用原生response对象返回了响应 不提供ModelAndView对象
            return null;
        }

        //不是Ajax请求 创建ModelAndView对象 将异常对象存入模型 返回页面
        ModelAndView mv=new ModelAndView();

        //将Exception对象存入模型
        mv.addObject(CrowdConstant.ATTR_NAME_EXCEPTION,exception);

        //设置对应视图名称
        mv.setViewName(viewName);

        //返回ModelAndView对象
        return mv;
    }

}
